using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;

using SceneEngine;
using System.ComponentModel;
using System.IO;

namespace EnginePipeline
{
    [ContentProcessor(DisplayName = "Model - Scene Editor")]
    public class EngineModelProcessor : ModelProcessor
    {
        List<string> fullTexturePaths = new List<string>();
        List<string> relativeTexturePaths = new List<string>();

        List<Vector3> vertices = new List<Vector3>();

        [Browsable(false)]
        public override float RotationX
        {
            get { return base.RotationX; }
            set { base.RotationX = value; }
        }
        [Browsable(false)]
        public override float RotationY
        {
            get { return base.RotationY; }
            set { base.RotationY = value; }
        }
        [Browsable(false)]
        public override float RotationZ
        {
            get { return base.RotationZ; }
            set { base.RotationZ = value; }
        }

        static IList<string> acceptableVertexChannelNames = new string[]
        {
            VertexChannelNames.TextureCoordinate(0),
            VertexChannelNames.Normal(0),
            VertexChannelNames.Binormal(0),
            VertexChannelNames.Tangent(0)
        };

        public override ModelContent Process(NodeContent input,
                                     ContentProcessorContext context)
        {
            FindVertices(input);

            ModelContent model = base.Process(input, context);

            int index = 0;

            for (int i = 0; i < fullTexturePaths.Count; i++)
            {
                Uri modelUri = new Uri(Path.GetFullPath(input.Identity.SourceFilename));
                Uri textureUri = new Uri(Path.GetFullPath(fullTexturePaths[i]));

                Uri relativeUri = modelUri.MakeRelativeUri(textureUri);

                string relativePath = Uri.UnescapeDataString(relativeUri.ToString());
                relativeTexturePaths.Add(relativePath);
            }

            foreach (ModelMeshContent mesh in model.Meshes)
            {
                foreach (ModelMeshPartContent meshPart in mesh.MeshParts)
                {
                    if (fullTexturePaths.Count > 0)
                    {
                        meshPart.Tag = fullTexturePaths[(int)MathHelper.Clamp(index++, 0, fullTexturePaths.Count - 1)];
                    }
                }
            }

            ModelInformation modelInfo = new ModelInformation();
            
            modelInfo.OriginalTexturePaths = fullTexturePaths;
            modelInfo.RelativeTexturePaths = relativeTexturePaths;

            modelInfo.Vertices = vertices;

            model.Tag = modelInfo;

            return model;
        }

        private void FindVertices(NodeContent node)
        {
            MeshContent mesh = node as MeshContent;

            if (mesh != null)
            {
                Matrix absoluteTransform = mesh.AbsoluteTransform;

                foreach (GeometryContent geometry in mesh.Geometry)
                {
                    foreach (int index in geometry.Indices)
                    {
                        Vector3 vertex = geometry.Vertices.Positions[index];
                        vertex = Vector3.Transform(vertex, absoluteTransform);
                        vertex = Vector3.Transform(vertex, Matrix.CreateScale(Scale));
                        vertices.Add(vertex);
                    }
                }
            }

            foreach (NodeContent child in node.Children)
            {
                FindVertices(child);
            }
        }

        protected override void ProcessVertexChannel(GeometryContent geometry, int vertexChannelIndex, 
                                                     ContentProcessorContext context)
        {
            String vertexChannelName = geometry.Vertices.Channels[vertexChannelIndex].Name;

            if (acceptableVertexChannelNames.Contains(vertexChannelName))
            {
                base.ProcessVertexChannel(geometry, vertexChannelIndex, context);
            }
            else
            {
                geometry.Vertices.Channels.Remove(vertexChannelName);
            }
        } 

        protected override MaterialContent ConvertMaterial(MaterialContent material,
                                                           ContentProcessorContext context)
        {
            BasicMaterialContent basicMaterial = material as BasicMaterialContent;

            if (basicMaterial == null)
            {
                throw new InvalidContentException(string.Format(
                    "MaterialProcessor only supports BasicMaterialContent, " +
                    "but input mesh uses {0}.", material.GetType()));
            }

            if (basicMaterial.Texture != null)
                fullTexturePaths.Add(basicMaterial.Texture.Filename);

            return base.ConvertMaterial(basicMaterial, context);
        }
    }
}
